/*
 * Copyright (C), 2002-2015, 你的兔宝宝电子商务有限公司
 * FileName: SNFileUtils.java
 * Author:   你的兔宝宝
 * Date:     2015-7-9 上午10:49:56
 * Description: //模块目的、功能描述      
 * History: //修改记录
 * <author>      <time>      <version>    <desc>
 * 修改人姓名             修改时间            版本号                  描述
 */
package com.baby.demo.utils;

import java.io.Closeable;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉
 * 
 * @author 你的兔宝宝
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class Files {

    private static final Logger LOGGER = LoggerFactory.getLogger(Files.class);
    
    private static final int DEFAULT_BUFFER_SIZE = 8192;
    
    /**
     * 文件大小正则
     */
    private static String file_size_re = "(\\d+)(T|G|M|K)B";
    private static Pattern file_size_reg = Pattern.compile(file_size_re, Pattern.CASE_INSENSITIVE);
    private static Map<String,Integer> file_size_unit = new HashMap<String,Integer>();
    
    /**
     * 路径和后缀正则
     */
    private static Pattern pathFileNameReg = Pattern.compile("(([^\\\\/]*?[\\\\/])+)([^\\?]+)(.*)", Pattern.CASE_INSENSITIVE);
    
    /**
     * 目录匹配
     */
    private static Pattern directoryRe = Pattern.compile(".*(/|\\\\)$", Pattern.CASE_INSENSITIVE);
    
    static{
        file_size_unit.put("K", 1);
        file_size_unit.put("M", 2);
        file_size_unit.put("G", 3);
        file_size_unit.put("T", 4);
    }
    
    
    private Files() {
        
    }
    
    public static long copyQuietly(URL url, String localFilePath) throws IOException {
        File outFile = new File(localFilePath);
        makedirs(outFile.getParentFile());
        FileOutputStream fos = new FileOutputStream(outFile);
        try {
            return copyQuietly(url.openStream(), fos, new byte[DEFAULT_BUFFER_SIZE]);
        } catch (Exception e) {
            LOGGER.error("copy error",e);
        }finally{
            closeQuietly(fos);
        }
        return -1;
    }

    /**
     * 
     * 功能描述: 复制流<br>
     * 〈功能详细描述〉
     *
     * @param pInputStream      输入流
     * @param pOutputStream     输出流
     * @return
     * @throws IOException
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static long copy(InputStream pInputStream, OutputStream pOutputStream) throws IOException {
        return copy(pInputStream, pOutputStream, new byte[DEFAULT_BUFFER_SIZE]);
    }
    
    public static long copyQuietly(InputStream pInputStream, OutputStream pOutputStream) throws IOException {
        return copyQuietly(pInputStream, pOutputStream, new byte[DEFAULT_BUFFER_SIZE]);
    }
    
    public static long copy(InputStream pInputStream, String outFilePath) throws IOException{
        File outFile = new File(outFilePath);
        makedirs(outFile.getParentFile());
        FileOutputStream fos = new FileOutputStream(outFile);
        long size =  copy(pInputStream, fos, new byte[DEFAULT_BUFFER_SIZE]);
        closeQuietly(fos);
        return size;
    }
    
    public static long copyQuietly(InputStream pInputStream, String outFilePath) throws IOException{
        File outFile = new File(outFilePath);
        makedirs(outFile.getParentFile());
        FileOutputStream fos = new FileOutputStream(outFile);
        return copyQuietly(pInputStream, fos, new byte[DEFAULT_BUFFER_SIZE]);
    }

    /**
     * 
     * 功能描述: 复制流<br>
     * 〈功能详细描述〉
     *
     * @param pIn
     * @param pOut
     * @param pBuffer
     * @return
     * @throws IOException
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static long copy(InputStream pIn, OutputStream pOut, byte[] pBuffer) throws IOException {
        return copy(pIn, pOut, pBuffer,false,false);
    }
    
    public static long copyQuietly(InputStream pIn, OutputStream pOut, byte[] pBuffer) throws IOException {
        return copy(pIn, pOut, pBuffer,true,true);
    }
    
    public static long copy(InputStream pIn, OutputStream pOut, byte[] pBuffer,boolean closeIn,boolean closeOut) throws IOException {
        OutputStream out = pOut;
        InputStream in = pIn;
        long total = 0L;
        try {
            while (true) {
                int res = in.read(pBuffer);
                if (res == -1) {
                    break;
                }
                if (res > 0) {
                    total += res;
                    if (out != null) {
                        out.write(pBuffer, 0, res);
                    }
                }
            }
            flush(out);
        } catch (Exception e) {
            LOGGER.error("copy error",e);
        }finally{
            if(closeOut){
                closeQuietly(pOut);
            }
            if(closeIn){
                closeQuietly(pIn);
            }
        }
        return total;
    }
    
    /**
     * 
     * 功能描述: 创建文件夹<br>
     * 〈功能详细描述〉
     *
     * @param file
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static boolean makedirs(File file){
        boolean success = true;
        if (!file.exists()) {
            success = file.mkdirs();
        }
        return success;
    }
    
    /**
     * 
     * 功能描述: 创建文件夹<br>
     * 〈功能详细描述〉
     *
     * @param file
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void makedirs(String file){
        makedirs(new File(file));
    }
    
    /**
     * 
     * 功能描述: 判断是否存在路径或者文件<br>
     * 〈功能详细描述〉
     *
     * @param pathfile
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static boolean existsdir(String pathfile){
        return new File(pathfile).exists();
    }
    
    public static String getFilename(String fileName){
        String filename = fileName;
        int at = filename.lastIndexOf(".");
        if( at > 0 ){
            filename = filename.substring(0,at);
        }
        return filename;
    }
    
    /**
     * 
     * 功能描述: 检测一个路径是否为目录或者文件不存在<br>
     * 〈功能详细描述〉
     *
     * @param pathfile
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static boolean isDirectory(String pathfile){
        boolean isdirectory = false;
        try{
            Matcher directoryM =  directoryRe.matcher(pathfile);
            isdirectory = directoryM.matches();
            if( !isdirectory ){
                File resource  = new File(pathfile);
                isdirectory = resource.isDirectory() || !resource.exists() ;
            }
        }catch (Exception e) {
            LOGGER.error("检测是否为目录错误",e);
        }
        return isdirectory;
    }
    
    /**
     * 
     * 功能描述: 关闭一个可关闭对象<br>
     * 〈功能详细描述〉
     *
     * @param closeable
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void closeQuietly(Closeable closeable){
        if( closeable != null){
            try {
                closeable.close();
            } catch (IOException e) {
                LOGGER.error("关闭输入/输出流异常",e);
            }
        }
    }
    
    /**
     * 
     * 功能描述: 刷新一个可刷新对象<br>
     * 〈功能详细描述〉
     *
     * @param flushable
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void flush(Flushable flushable){
        if( flushable != null){
            try {
                flushable.flush();
            } catch (IOException e) {
                LOGGER.error("刷新流异常",e);
            }
        }
    }
    
    /**
     * 
     * 功能描述: 关闭一个输出流<br>
     * 〈功能详细描述〉
     *
     * @param out
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void closeQuietly(OutputStream out){
        flush(out);
        closeQuietly((Closeable)out);
    }
    
    /**
     * 
     * 功能描述: 允许文件扩展<br>
     * 〈功能详细描述〉
     *
     * @param filename
     * @param exts
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static boolean allowExts(String filename, String exts){
        Pattern p = Pattern.compile("\\.("+exts+")$",Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(filename);
        return m.find();
    }
    
    /**
     * 
     * 功能描述: 其他单位转Bytes<br>
     * 〈功能详细描述〉
     *
     * @param size
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static long size2bytes(String size){
        long osize = 0L;
        Matcher m = file_size_reg.matcher(size);
        if( m.find() ){
            m.group();
            int tsize = Strings.getInt(m.group(1), 0);
            String unit = m.group(2).toUpperCase();
            osize = (long) (tsize * Math.pow(1024, file_size_unit.get(unit)));
        }
        return osize;
    }
    
    /**
     * 
     * 功能描述: 获取文件信息<br>
     * 〈功能详细描述〉
     *
     * @param pathFileName
     * @return  {path/fileName/name/suffix}
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static Map<String,String> getFileInfo(String pathFileName){
        Map<String,String> fileInfo = new HashMap<String,String>();
        fileInfo.put("pathfile", pathFileName);
        Matcher mPathFileName = pathFileNameReg.matcher(pathFileName);
        //路径-不包含文件名
        if( mPathFileName.matches() ){
            mPathFileName.group();
            String path = mPathFileName.group(1);
            String fileName = mPathFileName.group(3);
            fileInfo.put("path", path);
            fileInfo.put("fileName", fileName);
            //文件名
            //文件后缀
            if( Strings.isNotBlank(fileName) ){
                int at = fileName.lastIndexOf(".");
                if( at >=0 ){
                    String suffix = fileName.substring(at+1);
                    String name = fileName.substring(0,at);
                    fileInfo.put("name", name);
                    fileInfo.put("suffix", suffix);
                }
            }
        }
        return fileInfo;
    }
    
    /**
     * 
     * 功能描述: 获取文件后缀<br>
     * 〈功能详细描述〉
     *
     * @param filename
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static String getExts(String filename){
        String suffix = Strings.EMPTY;
        //文件后缀
        if( Strings.isNotBlank(filename) ){
            int at = filename.lastIndexOf(".");
            if( at >=0 ){
                suffix = filename.substring(at+1);
            }
        }
        return suffix;
    }
    
    /**
     * 读文件 〈一句话功能简述〉 〈功能详细描述〉
     * 
     * @14040873
     * @param path
     * @return
     * @see [相关类/方法]（可选）
     * @since [产品/模块版本] （可选）
     */
    public static String readFile(String path) {
        File file = new File(path);
        return readFile(file);
    }

    /**
     * 读文件
     * 
     * @14040873
     * @param file
     * @return
     */
    public static String readFile(File file) {
        String str = "";
        try {
            str = FileUtils.readFileToString(file, "UTF-8");
        } catch (IOException e) {
            LOGGER.error("读取文件失败：" + e.getMessage(),e);
        }
        return str;
    }
    
    /**
     * 
     * 功能描述: 转成输入流<br>
     * 〈功能详细描述〉
     *
     * @param url
     * @return
     * @throws IOException
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static InputStream toInputStream(URL url) throws IOException{
        InputStream is = url.openConnection().getInputStream();
        return is;
    }
    
    /**
     * 
     * 功能描述: 转成输入流<br>
     * 〈功能详细描述〉
     *
     * @param surl
     * @return
     * @throws IOException
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static InputStream toInputStream(String surl) throws IOException{
        URL url = new URL(surl);
        InputStream is = url.openConnection().getInputStream();
        return is;
    }
    
    
    
    /**
     * 文件处理回调
     * 〈一句话功能简述〉<br> 
     * 〈功能详细描述〉
     *
     * @author 你的兔宝宝
     * @see [相关类/方法]（可选）
     * @since [产品/模块版本] （可选）
     */
    public static class FileCallback{
        public void eachFile(File file){}
    }
    
    /**
     * 
     * 功能描述: 遍历目录下的文件<br>
     * 〈功能详细描述〉
     *
     * @param directory
     * @param exts
     * @param cb
     * @Author:   你的兔宝宝
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void eachFiles(String directory,final String exts,FileCallback cb){
        File dir = new File(directory);
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                String name = file.getName();
                boolean accept = allowExts(name, exts) || (!name.startsWith(".") && file.isDirectory());
                return accept;
            }
        });
        for(File file:files){
            if( file.isDirectory() ){
                eachFiles(file.getAbsolutePath(), exts,cb);
            }else{
                cb.eachFile(file);
            }
        }
    }
    
    
}
